package com.gitee.feizns.quickstart.mybatis.plus.spring.boot.autoconfigure;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.gitee.feizns.dynamic.reflect.Constructors;
import com.gitee.feizns.quickstart.domain.page.PageAo;
import com.gitee.feizns.quickstart.domain.page.PageRecordsVo;
import com.gitee.feizns.quickstart.mybatis.plus.query.Page;
import com.gitee.feizns.quickstart.mybatis.plus.query.Query;
import com.gitee.feizns.quickstart.mybatis.plus.utils.Mappers;
import com.gitee.feizns.quickstart.validation.ValidationUtils;
import com.gitee.feizns.quickstart.web.restful.RestExecutor;
import com.gitee.feizns.quickstart.web.restful.Rests;
import com.gitee.feizns.quickstart.web.restful.ex.ResourceNotFoundException;
import com.gitee.feizns.quickstart.web.restful.listener.Creatable;
import com.gitee.feizns.quickstart.web.restful.listener.RestListener;
import com.gitee.feizns.quickstart.web.restful.validation.Create;
import com.gitee.feizns.quickstart.web.restful.validation.Patch;
import com.gitee.feizns.quickstart.web.restful.validation.PatchById;
import com.gitee.feizns.quickstart.web.restful.validation.Put;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 快速入门mybatis impl + rest式服务
 * @author feizns
 * @since 2023/04/21
 */
public class QuickstartMybatisPlusRestExecutorImpl implements RestExecutor {

    @Override
    public boolean existsId(Class<?> recordType, Serializable id) {
        TableInfo info = TableInfoHelper.getTableInfo(recordType);
        @SuppressWarnings("unchecked")
        BaseMapper<Object> mapper = (BaseMapper<Object>) Mappers.of(recordType);
        return mapper.exists(Wrappers.query().eq(info.getKeyColumn(), id));
    }

    /**
     * 创建数据
     * @param aos 记录
     * @param recordClass record 类
     * @return {@link List}<{@link T}>
     */
    @Override
    public <T> List<T> create(Class<T> recordClass, Object... aos) {
        //获取Mapper
        BaseMapper<T> mapper = Mappers.of(recordClass);
        return Arrays.stream(aos).map(ao -> {
            //Creatable
            if ( ao instanceof Creatable ) {
                //noinspection SingleStatementInBlock,unchecked
                return ((Creatable<T>) ao).create();
            }
            //创建实例
            T record = Constructors.newInstance(recordClass);
            //复制属性
            BeanUtils.copyProperties(ao, record);
            //校验请求体
            ValidationUtils.validateIfFailsThenThrow(record, Create.class);
            //获取监听器
            RestListener<T> listener = Rests.restListener(recordClass);
            //新增之前事件
            if ( listener != null )
                record = listener.beforeCreate(record);
            //插入数据库
            mapper.insert(record);
            //新增之后事件
            if ( listener != null )
                record = listener.created(record);
            //返回
            return record;
        }).collect(Collectors.toList());
    }

    @Override
    public <T> T putById(Class<T> recordType, Object ao, Serializable id) {
        T record = Constructors.newInstance(recordType);
        //复制数据到目标record
        BeanUtils.copyProperties(ao, record);
        //设置编号
        Rests.setId(record, id);
        //存储
        return CollectionUtils.firstElement(put(recordType, record));
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T putById(T record, Serializable id) {
        //获取类型
        Class<T> recordClass = (Class<T>) record.getClass();
        //存储
        return putById(recordClass, record, id);
    }

    @Override
    public <T> List<T> put(Class<T> recordClass, Object... aos) {
        TableInfo info = TableInfoHelper.getTableInfo(recordClass);
        BaseMapper<T> mapper = Mappers.of(recordClass);
        return Arrays.stream(aos).map(ao -> {
            //创建实例
            T record = Constructors.newInstance(recordClass);
            //复制属性
            BeanUtils.copyProperties(ao, record);
            //校验数据
            ValidationUtils.validateIfFailsThenThrow(record, Put.class);
            //获取编号
            Serializable id = Rests.getId(record);
            //获取监听器
            RestListener<T> listener = Rests.restListener(recordClass);
            //插入或更新
            if ( mapper.exists(Wrappers.<T>query().eq(info.getKeyColumn(), id)) ) {
                //更新前置回调
                if ( listener != null )
                    record = listener.beforeUpdateById(record);
                //更新
//                mapper.alwaysUpdateSomeColumnById(record);
                //更新
                mapper.updateById(record);
                //更新后置回调
                if ( listener != null )
                    record = listener.updatedById(record);
            } else {
                //新增前置回调
                if ( listener != null )
                    record = listener.beforeCreate(record);
                //新增
                mapper.insert(record);
                //新增前置回调
                if ( listener != null )
                    record = listener.created(record);
            }
            return record;
        }).collect(Collectors.toList());
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T patchById(T record, Serializable id) {
        //实体类型
        Class<T> recordClass = (Class<T>) record.getClass();
        //校验数据
        ValidationUtils.validateIfFailsThenThrow(record, PatchById.class);
        //获取Mapper
        BaseMapper<T> mapper = Mappers.of(recordClass);
        //获取监听器
        RestListener<T> listener = Rests.restListener(recordClass);
        if ( listener != null )
            record = listener.beforeUpdateById(record);
        //更新
        Rests.setId(record, id);
        int updated = mapper.updateById(record);
        if ( updated == 0 )
            throwsResourceNotFoundException(id);
        //修改之后回调
        if ( listener != null )
            listener.updatedById(record);
        //返回
        return getById(recordClass, id);
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> long patch(T record, Object ao) {
        //实体类型
        Class<T> recordClass = (Class<T>) record.getClass();
        //校验数据
        ValidationUtils.validateIfFailsThenThrow(record, Patch.class);
        //获取Mapper
        BaseMapper<T> mapper = Mappers.of(recordClass);
        //获取监听器
        RestListener<T> listener = Rests.restListener(recordClass);
        if ( listener != null )
            listener.beforeUpdate(ao);
        //自动构建条件进行更新
        return mapper.update(record, Query.of(ao));
    }

    @Override
    public <T> T deleteById(Class<T> recordClass, Serializable id) {
        //获取原数据
        T original = getById(recordClass, id);
        //获取Mapper
        BaseMapper<T> mapper = Mappers.of(recordClass);
        //获取监听器
        RestListener<T> listener = Rests.restListener(recordClass);
        //前置回调
        if ( listener != null )
            original = listener.beforeDeleteById(original);
        //删除
        mapper.deleteById(id);
        //后置回调
        if ( listener != null )
            original = listener.deletedById(original);
        //返回
        return original;
    }

    @Override
    public <T> long delete(Class<T> recordClass, Object ao) {
        //获取Mapper
        BaseMapper<T> mapper = Mappers.of(recordClass);
        //删除之前回调
        RestListener<T> listener = Rests.restListener(recordClass);
        if ( listener != null )
            listener.beforeDelete(ao);
        //删除
        return mapper.delete(Query.of(ao));
    }

    @Override
    public <T> T getById(Class<T> recordType, Serializable id) {
        //获取Mapper
        BaseMapper<T> mapper = Mappers.of(recordType);
        //获取数据
        T result = mapper.selectById(id);
        if ( result == null )
            throw new ResourceNotFoundException(String.format("数据不存在：编号[%s].", id));
        return result;
    }

    @Override
    public <T> PageRecordsVo<T> page(Class<T> recordType, PageAo page, Object... aos) {
        BaseMapper<T> mapper = Mappers.of(recordType);
        Page<T> vo = Page.of(page);
        Wrapper<T> query = Query.of(Wrappers.query(), aos);
        mapper.selectPage(vo, query);
        return vo.toPageRecordsVo();
    }

}
